﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Sonic.Framework.Interfaces;
using Sonic.Framework.Enums;

namespace Sonic.Framework.Site
{
    public class SiteDirectory : ISiteDirectory
    {
        /// <summary>
        /// Files to exclude from certain processes within this directory
        /// </summary>
        private List<IExclusion> _exclusions = new List<IExclusion>();
        public List<IExclusion> Exclusions
        {
            get
            {
                return _exclusions;
            }
            set
            {
                _exclusions = value;
            }
        }

        /// <summary>
        /// A relative directory path within the site structure
        /// </summary>
        public string RelativePath
        {
            get;
            set;
        }

        /// <summary>
        /// The directory to process
        /// </summary>
        public DirectoryInfo DirectoryInfo
        {
            get;
            set;
        }

        /// <summary>
        /// An array of file extensions to be processed for this directory
        /// </summary>
        public string[] FileExtensions
        {
            get;
            set;
        }

        /// <summary>
        /// The tool to run over files within this directory that match the FileExtensions array
        /// </summary>
        public IProcessorTool Tool
        {
            get;
            set;
        }

        // ProcessorToolDirectives to run pre exec
        private List<ProcessorToolDirectiveCall> _preProcessorDirectives = new List<ProcessorToolDirectiveCall>();
        public List<ProcessorToolDirectiveCall> PreProcessorDirectives
        {
            get
            {
                return _preProcessorDirectives;
            }
            set
            {
                _preProcessorDirectives = value;
            }
        }

        // ProcessorToolDirectives to run post exec
        private List<ProcessorToolDirectiveCall> _postProcessorDirectives = new List<ProcessorToolDirectiveCall>();
        public List<ProcessorToolDirectiveCall> PostProcessorDirectives {
            get
            {
                return _postProcessorDirectives;
            }
            set
            {
                _postProcessorDirectives = value;
            }
        }


        public SiteDirectory() { }


        public void Exclude(FileInfo file, Enums.ExclusionType exclusionType)
        {
            Exclude(file, exclusionType, null);
        }

        /// <summary>
        /// Exclude a file from the processor or directive plugin tools for this directory
        /// </summary>
        /// <param name="file"></param>
        /// <param name="exclusionType"></param>
        /// <param name="processorToolDirectiveName"></param>
        public void Exclude(FileInfo file, Enums.ExclusionType exclusionType, string processorToolDirectiveName)
        {
            this.Exclusions.Add(new Exclusion(file, exclusionType, processorToolDirectiveName));
        }

        /// <summary>
        /// Exclude a collection of files from the processor or directive plugin tools for this directory
        /// </summary>
        /// <param name="file"></param>
        /// <param name="exclusionType"></param>
        /// <param name="processorToolDirectiveName"></param>
        public void Exclude(List<FileInfo> files, Enums.ExclusionType exclusionType, string processorToolDirectiveName)
        {
            this.Exclusions.Add(new Exclusion(files, exclusionType, processorToolDirectiveName));
        }

        public void Exclude(DirectoryInfo dir, Enums.ExclusionType exclusionType)
        {
            this.Exclude(dir, exclusionType, null);
        }

        public void Exclude(DirectoryInfo dir, Enums.ExclusionType exclusionType, string processorToolDirectiveName)
        {
            foreach (FileInfo file in dir.GetFiles("*", SearchOption.AllDirectories))
            {
                this.Exclusions.Add(new Exclusion(file, exclusionType, processorToolDirectiveName));
            }
        }

        /// <summary>
        /// Get files for the directory excluding some types
        /// </summary>
        /// <param name="excludeTypes"></param>
        /// <returns></returns>
        public List<FileInfo> GetMatchingFiles(ExclusionType exclusionType)
        {
            List<FileInfo> matchingFileInfos = new List<FileInfo>();

            // Get all files that match the extensions configured for this directory
            foreach (string fileExtension in FileExtensions)
            {
                matchingFileInfos.AddRange(DirectoryInfo.GetFiles(fileExtension, SearchOption.AllDirectories));
            }

            // Remove explicitly excluded files
            foreach (Site.Exclusion exclusion in Exclusions)
            {
                if (exclusion.ExclusionType == exclusionType)
                {
                    foreach (FileInfo fileToExlude in exclusion.FilesToExclude)
                    {
                        // Exhaustively check all files for matches
                        for(int i=0; i<matchingFileInfos.Count; i++)
                        {
                            FileInfo matchingFileInfo = matchingFileInfos[i];

                            if (matchingFileInfo.FullName.ToLower() == fileToExlude.FullName.ToLower())
                            {
                                matchingFileInfos.Remove(matchingFileInfo);

                                i--; // decrement i to keep current index correct on next loop

                                NLogSingleton.Instance.Logger.Trace("Excluding " + fileToExlude.FullName + " of type " + exclusionType.ToString());
                            }
                        }
                    }
                }
            }

            return matchingFileInfos;
        }

        public List<FileInfo> GetMatchingFiles(ExclusionType exclusionType, string processorToolDirectiveName)
        {
            List<FileInfo> matchingFileInfos = new List<FileInfo>();

            // Get all files that match the extensions configured for this directory
            foreach (string fileExtension in FileExtensions)
            {
                matchingFileInfos.AddRange(DirectoryInfo.GetFiles(fileExtension, SearchOption.AllDirectories));
            }

            // Remove explicitly excluded files
            foreach (Site.Exclusion exclusion in Exclusions)
            {
                if (exclusion.ExclusionType == exclusionType && exclusion.ProcessorToolDirectiveName.ToLower() == processorToolDirectiveName.ToLower())
                {
                    foreach (FileInfo fileToExlude in exclusion.FilesToExclude)
                    {
                        // Exhaustively check all files for matches
                        for (int i = 0; i < matchingFileInfos.Count; i++)
                        {
                            FileInfo matchingFileInfo = matchingFileInfos[i];

                            if (matchingFileInfo.FullName.ToLower() == fileToExlude.FullName.ToLower())
                            {
                                matchingFileInfos.Remove(matchingFileInfo);

                                i--; // decrement i to keep current index correct on next loop

                                NLogSingleton.Instance.Logger.Trace("Excluding " + fileToExlude.FullName + " of type " + exclusionType.ToString());
                            }
                        }
                    }
                }
            }

            return matchingFileInfos;
        }
    }
}
